package gov.nasa.anml.parsing;

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

/** A TreeAdaptor that works with any Tree implementation.  It provides
 *  really just factory methods; all the work is done by BaseTreeAdaptor.
 *  If you would like to have different tokens created than ClassicToken
 *  objects, you need to override this and then set the parser tree adaptor to
 *  use your subclass.
 *
 *  To get your parser to build nodes of a different type, override
 *  create(Token).
 */
public class ANMLTreeAdaptor extends BaseTreeAdaptor {
	/** Duplicate a node.  This is part of the factory;
	 *	override if you want another kind of node to be built.
	 *
	 *  I could use reflection to prevent having to override this
	 *  but reflection is slow.
	 */
	public String[] tokenNames=null;
	
	public ANMLTreeAdaptor(String[] tokenNames) {
		this.tokenNames = tokenNames;
	}
	
	public ANMLToken dupNode(Object t) {
		if ( t==null ) {
			return null;
		}
		return ((ANMLToken)t).dupNode();
	}

	// might not be necessary to copy??
	public ANMLToken create(Token payload) {
		return new ANMLToken(payload);
	}

	@Override
	public ANMLToken create(int tokenType, Token fromToken) {
		if (tokenType != fromToken.getType()) {
			ANMLToken tree = new ANMLToken(tokenType,tokenNames[tokenType]+"_l"+fromToken.getLine()+"_c"+fromToken.getCharPositionInLine());
			return tree;
		}
		return new ANMLToken(fromToken);
	}


	@Override
	public ANMLToken create(int tokenType, String text) {
		return createToken(tokenType,text);
	}

	@Override
	public ANMLToken create(int tokenType, Token fromToken, String text) {
		ANMLToken ret = new ANMLToken(fromToken);
		ret.setType(tokenType);
		ret.setText(text);
		return ret;
	}

	/** Tell me how to create a token for use with imaginary token nodes.
	 *  For example, there is probably no input symbol associated with imaginary
	 *  token DECL, but you need to create it as a payload or whatever for
	 *  the DECL node as in ^(DECL type ID).
	 *
	 *  If you care what the token payload objects' type is, you should
	 *  override this method and any other createToken variant.
	 */
	@Override
	public ANMLToken createToken(int tokenType, String text) {
		return new ANMLToken(tokenType, text);
	}

	/** Tell me how to create a token for use with imaginary token nodes.
	 *  For example, there is probably no input symbol associated with imaginary
	 *  token DECL, but you need to create it as a payload or whatever for
	 *  the DECL node as in ^(DECL type ID).
	 *
	 *  This is a variant of createToken where the new token is derived from
	 *  an actual real input token.  Typically this is for converting '{'
	 *  tokens to BLOCK etc...  You'll see
	 *
	 *    r : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ;
	 *
	 *  If you care what the token payload objects' type is, you should
	 *  override this method and any other createToken variant.
	 */
	@Override
	public ANMLToken createToken(Token fromToken) {
		return new ANMLToken(fromToken);
	}
	
	/** Track start/stop token for subtree root created for a rule.
	 *  Only works with Tree nodes.  For rules that match nothing,
	 *  seems like this will yield start=i and stop=i-1 in a nil node.
	 *  Might be useful info so I'll not force to be i..i.
	 */
	public void setTokenBoundaries(Object t, Token startToken, Token stopToken) {
		if ( t==null ) {
			return;
		}
		int start = 0;
		int stop = 0;
		if ( startToken!=null ) {
			start = startToken.getTokenIndex();
		}
		if ( stopToken!=null ) {
			stop = stopToken.getTokenIndex();
		}
		((Tree)t).setTokenStartIndex(start);
		((Tree)t).setTokenStopIndex(stop);
	}

	public int getTokenStartIndex(Object t) {
		if ( t==null ) {
			return -1;
		}
		return ((Tree)t).getTokenStartIndex();
	}

	public int getTokenStopIndex(Object t) {
		if ( t==null ) {
			return -1;
		}
		return ((Tree)t).getTokenStopIndex();
	}

	public String getText(Object t) {
		if ( t==null ) {
			return null;
		}
		return ((Tree)t).getText();
	}

	public int getType(Object t) {
		if ( t==null ) {
			return Token.INVALID_TOKEN_TYPE;
		}
		return ((Tree)t).getType();
	}

	/** What is the Token associated with this node?  If
	 *  you are not using ANMLToken, then you must
	 *  override this in your own adaptor.
	 */
	public Token getToken(Object t) {
		if ( t instanceof ANMLToken ) {
			return ((ANMLToken)t).getToken();
		}
		return null; // no idea what to do
	}

	public Object getChild(Object t, int i) {
		if ( t==null ) {
			return null;
		}
		return ((Tree)t).getChild(i);
	}

	public int getChildCount(Object t) {
		if ( t==null ) {
			return 0;
		}
		return ((Tree)t).getChildCount();
	}

	public Object getParent(Object t) {
		return ((Tree)t).getParent();
	}

	public void setParent(Object t, Object parent) {
		((Tree)t).setParent((Tree)parent);
	}

	public int getChildIndex(Object t) {
		return ((Tree)t).getChildIndex();
	}

	public void setChildIndex(Object t, int index) {
		((Tree)t).setChildIndex(index);
	}

	public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) {
		if ( parent!=null ) {
			((Tree)parent).replaceChildren(startChildIndex, stopChildIndex, t);
		}
	}

	public Object errorNode(TokenStream input, Token start, Token stop,
			RecognitionException e)
	{
		ANMLErrorNode t = new ANMLErrorNode(input, start, stop, e);
//		System.out.println("returning error node '"+t+"' @index="+input.index());
		return t;
	}

}
